#if PocketPC && !Framework20
using System;
using System.Collections;
using System.Runtime.InteropServices;
using GeoFramework.IO;

namespace GeoFramework
{
	[StructLayout(LayoutKind.Sequential)]
	internal struct SECURITY_ATTRIBUTES
	{
		public int nLength;
		public IntPtr lpSecurityDescriptor;
		public bool bInheritHandle;
	}

	[Flags]
	internal enum RegOption
	{
		NonVolatile = 0x0,
		Volatile = 0x1,
		CreateLink = 0x2,
		BackupRestore = 0x4,
		OpenLink = 0x8
	}

	[Flags]
	internal enum RegSAM
	{
		QueryValue = 0x0001,
		SetValue = 0x0002,
		CreateSubKey = 0x0004,
		EnumerateSubKeys = 0x0008,
		Notify = 0x0010,
		CreateLink = 0x0020,
		WOW64_32Key = 0x0200,
		WOW64_64Key = 0x0100,
		WOW64_Res = 0x0300,
		Read = 0x00020019,
		Write = 0x00020006,
		Execute = 0x00020019,
		AllAccess = 0x000f003f
	}

	internal enum KeyDisposition
	{
		CreatedNewKey = 0x00000001,
		OpenedExistingKey = 0x00000002
	}
	
	[CLSCompliant(false)]
	public enum RootKey : uint
	{
		HKEY_CLASSES_ROOT = 0x80000000,
		HKEY_CURRENT_USER = 0x80000001,
		HKEY_LOCAL_MACHINE = 0x80000002,
		HKEY_USERS = 0x80000003
	}

    public enum RegistryValueKind
    {
        Binary,
        DWord,
        ExpandString,
        MultiString,
        String,
        QWord,
        Unknown
    }

	///	<summary>		
	///	Represents a key level node	in the Windows registry. This class	is a registry encapsulation.		
	///	</summary>		
	public sealed class RegistryKey 
#if Framework20
        : MarshalByRefObject
#endif
	{
		private SafeRegistryHandle pHandle;
		private RootKey pRootKey;
		private string pName;
		//private bool pReadOnly;

		internal const int REG_SZ = 1;
		internal const int REG_EXPAND_SZ = 2;
		internal const int REG_BINARY = 3;
		internal const int REG_DWORD = 4;
	
		internal RegistryKey(RootKey root)
		{
			//pHandle = SafeRegistryHandle.Invalid;
			pRootKey = root;
			//pReadOnly = true;
			pName = "";
		}

		internal RegistryKey(RootKey root, SafeRegistryHandle handle, string name)
		{
			pHandle = handle;
			pRootKey = root;
			//pReadOnly = false;
			pName = name;
		}

		///	<summary>			
		///	Closes the key and flushes it to disk if the contents have been	modified.			
		///	</summary>			
		public void Close()
		{
			if(pHandle != null)
				pHandle.Close();
		}

		///	<summary>			
		///	Creates	a new subkey or	opens an existing subkey. The string subKey	is not case-sensitive.				
		///	</summary>			
		///	<param name="subkey">Name or path of subkey	to create or open. </param>			
		///	<returns>Returns the subkey, or	null if	the	operation failed.</returns>			
		public RegistryKey CreateSubKey(string subKey)
		{
			int Reserved = 0;
			int Disposition = 0;
			try
			{
				// Create the new key in the registry
				SafeRegistryHandle Result = NativeMethods.RegCreateKeyEx(pRootKey, pName + @"\" + subKey, 0, null, 0, 0, ref Reserved, ref Disposition);
				// And return a new RegistryKey object
				if(Result == null)
					return null;
				else
					return new RegistryKey(pRootKey, Result, pName + @"\" + subKey);				
			}
			catch
			{
				throw;
			}
		}

		///	<summary>			
		///	Deletes	the	specified subkey. The string subkey	is not case-sensitive.			
		///	</summary>			
		///	<param name="subkey">Name of the subkey	to delete.</param>			
		///	<param name="throwOnMissing">Indicates whether an exception	should be raised			
		///	if the specified subkey	cannot be found. If	this argument is true and the			
		///	specified subkey does not exist	then an	exception is raised. If	this argument			
		///	is false and the specified subkey does not exist, then no action is	taken			
		///	</param>			
		public void DeleteSubKey(string subKey, bool throwOnMissingSubKey)
		{
			// Does the key actually exist?
			RegistryKey CheckKey = null;
			try
			{
				CheckKey = this.OpenSubKey(subKey);
				// If not, and we're allow
				if(CheckKey == null)
				{
					if(throwOnMissingSubKey)
						throw new InvalidOperationException("The specified registry key cannot be deleted because it does not exist.");
					else
						return;
				}
				// The key exists.  Does it have children?
				if(SubKeyCount > 0)
				{
					if(throwOnMissingSubKey)
						throw new InvalidOperationException("The specified registry key cannot be deleted because it contains sub-keys.");
					else
						return;
				}
			}
			catch
			{
				throw;
			}
			finally
			{
				// Close the key before deleting
				if(CheckKey != null)
					CheckKey.Close();
			}			
			// The key exists and has no children, so delete it
			NativeMethods.RegDeleteKey(pHandle, subKey);
		}

		///	<summary>			
		///	Deletes	the	specified subkey. The string subkey	is not case-sensitive.			
		///	</summary>			
		///	<param name="subkey">Name of the subkey	to delete.</param>			
		public void DeleteSubKey(string subkey)
		{
			// Defer to the overloaded function				
			DeleteSubKey(subkey, true);
		}

		///	<summary>			
		///	Deletes	a subkey and any child subkeys recursively.	The	string subKey is not case-sensitive.			
		///	</summary>			
		///	<param name="subkey">Subkey	to delete</param>			
		public void DeleteSubKeyTree(string subkey)
		{
			// Append the sub-key to our path, then delete
			NativeMethods.RegDeleteKey(pHandle, subkey);
		}

		///	<summary>			
		///	Deletes	the	specified value	from this key. The string subKey is	not	case sensitive.			
		///	</summary>			
		///	<param name="val">Name of the value	to delete.</param>			
		///	<param name="throwOnMissing">Indicates whether an exception	should be raised			
		///	if the specified value cannot be found.	If this	argument is	true and the specified			
		///	value does not exist then an exception is raised. If this argument is false	and				
		///	the	specified value	does not exist,	then no	action is taken</param>			
		public void DeleteValue(string val, bool throwOnMissing)
		{
			if (throwOnMissing)
			{
				// do the check	to see if the key actually exists					
				int type = 0;
				int size = 0;
				byte[] data = null;
				if (NativeMethods.RegQueryValueEx(pHandle, val, 0, ref type, data, ref size) != 0)
					throw new Exception("The value " + val + " cannot be deleted because it	is missing.");
			}

			NativeMethods.RegDeleteValue(pHandle, val);
		}

		///	<summary>
		///	Deletes	the	specified value	from this key. The string subKey is	not	case sensitive.			
		///	</summary>			
		///	<param name="val">Name of the value	to delete..</param>			
		public void DeleteValue(string val)
		{
			// just	call the overloaded	function
			DeleteValue(val, false);
		}

		///	<summary>			
		///	Retrieves the count	of subkeys at the base level, for the current key.			
		///	</summary>
		public int SubKeyCount
		{
			get
			{
				int Subkeys = 0;
				int nullStuff = 0;
				char[] test = null;
				// query the registry key for the number of	subkeys
				int retVal = NativeMethods.RegQueryInfoKey(pHandle, test, ref nullStuff, 0,
					ref Subkeys, ref nullStuff, ref nullStuff,
					ref	nullStuff, ref nullStuff, ref nullStuff, 0, 0);
				return Subkeys;
			}
		}

		///	<summary>			
		///	Retrieves an array of strings that contains	all	the	subkey names.			
		///	</summary>			
		///	<returns>An	array of strings that contains the names of	the	subkeys	for	the	current	key.</returns>			
		public string[] GetSubKeyNames()
		{
			// create an arraylist to temporarily hold our subkey names				
			ArrayList subKeyNames = new ArrayList();
			int nullStuff = 0;
			int iMaxSubKeyLen = 255;
			char[] chNull = null;
			int iSubKeyLen = 0;
			char[] chSubKeyName = new char[iMaxSubKeyLen];
			for (int x = 0; x < this.SubKeyCount; x++)
			{
				// call	RegEnumKeyEx for as	many subkeys there are					
				iSubKeyLen = iMaxSubKeyLen;
				NativeMethods.RegEnumKeyEx(pHandle, x, chSubKeyName, ref iSubKeyLen, 0, chNull, ref nullStuff, 0);
				subKeyNames.Add(new string(chSubKeyName, 0, iSubKeyLen));
			}
			// return the array	as a string	Array				
			return (string[])subKeyNames.ToArray(typeof(string));
		}

		///	<summary>			
		///	Retrieves the count	of values in the key.			
		///	</summary>			
		public int ValueCount
		{
			get
			{
				int icValueKeys = 0;
				int nullStuff = 0;
				char[] test = null;
				// query the registry for the number of	values in the current key.
				int retVal = NativeMethods.RegQueryInfoKey(pHandle, test, ref nullStuff,
					0, ref nullStuff,
					ref	nullStuff, ref nullStuff,
					ref	icValueKeys, ref nullStuff,
					ref	nullStuff, 0, 0);
				return icValueKeys;
			}
		}

		///	<summary>			
		///	Retrieves an array of strings that contains	all	the	value names	associated with	this key.			
		///	</summary>			
		///	<returns>An	array of strings that contains the value names for the current key.</returns>
		public string[] GetValueNames()
		{
			// array list to temporarily hold the names	of the values				
			ArrayList valueNames = new ArrayList();
			int NameLen = 260;
			string pValueName = new string(' ', NameLen);


			int iMaxValueNameLen = 4096;
			byte[] byData = new byte[iMaxValueNameLen];
			int iDataLen = 0;
			int iType = 0;
			int iValueNameLen = 0;
			char[] chValueName = new char[260];
			for (int x = 0; x < this.ValueCount; x++)
			{
				// call	RegEnumKeyEx for all the values	in the
				iValueNameLen = iMaxValueNameLen;
				int resultTTY = NativeMethods.RegEnumValue(pHandle, x, chValueName, ref iValueNameLen, 0, ref iType, ref byData, ref iDataLen);
				if (resultTTY == 234)
				{
					byData = new byte[iDataLen];
					resultTTY = NativeMethods.RegEnumValue(pHandle, x, chValueName, ref iValueNameLen, 0, ref iType, ref byData, ref iDataLen);
				}
				valueNames.Add(new string(chValueName, 0, iValueNameLen));
			}
			return (string[])valueNames.ToArray(typeof(string));
		}

		///	<summary>			
		///	Retrieves the specified	value, or the default value	you	provide	if the specified value is not found.			
		///	</summary>			
		///	<param name="val">Name of the value	to retrieve.</param>			
		///	<param name="initialValue">Value to	return if name does	not	exist.</param>			
		///	<returns>The data associated with name,	or defaultValue	if name	is not found.</returns>			
		public object GetValue(string val, object initialValue)
		{
			int iType = 0;
			int icData = 0;
			byte[] byData = null;
			string sz = "";

			// read	in the value information, if no	value exists, just return the initial value	specified				
			if (NativeMethods.RegQueryValueEx(pHandle, val, 0, ref iType, byData, ref icData) != 0)
				return initialValue;

			byData = new byte[icData];

			if (NativeMethods.RegQueryValueEx(pHandle, val, 0, ref iType, byData, ref icData) != 0)
				return initialValue;

			// Switch the type we have,	and	convert	to the proper type				
			switch (iType)
			{
				case REG_BINARY:
					return byData;
				case REG_DWORD:
					return System.BitConverter.ToInt32(byData, 0);
				case REG_SZ:
					sz = System.Text.UnicodeEncoding.Unicode.GetString(byData, 0, icData);
					return sz.Substring(0, sz.IndexOf("\0"));
				case REG_EXPAND_SZ:
					sz = System.Text.UnicodeEncoding.Unicode.GetString(byData, 0, icData);
					return sz.Substring(0, sz.IndexOf("\0"));
				default:
					throw new Exception("Unknown registry type!");
			}
		}

		public object GetValue(string val)
		{
			// just	call the overloaded	function				
			return GetValue(val, null);
		}

		public void SetValue(string name, object data)
		{
			// find	out	what type of data we are setting, and call the appropriate RegSetValueEx function				
			if (data is string)
			{
                SetValue(name, data, RegistryValueKind.String);
			}
            else if (data is byte[])
			{
                SetValue(name, data, RegistryValueKind.Binary);
			}
            else if (data is int)
			{
                SetValue(name, data, RegistryValueKind.DWord);
			}
			else
			{
				throw new Exception("Unknown type to set!");
			}
		}

        public void SetValue(string name, object data, RegistryValueKind kind)
        {
            // find	out	what type of data we are setting, and call the appropriate RegSetValueEx function				
            switch (kind)
            {
                case RegistryValueKind.String:
                    byte[] StringData = System.Text.Encoding.Unicode.GetBytes(data + "\0");
                    NativeMethods.RegSetValueEx(pHandle, name, 0, REG_SZ, StringData, StringData.Length);
                    break;
                case RegistryValueKind.DWord:
                    byte[] DWordData = System.BitConverter.GetBytes((int)data);
                    NativeMethods.RegSetValueEx(pHandle, name, 0, REG_DWORD, DWordData, DWordData.Length);
                    break;
                case RegistryValueKind.Binary:
                    byte[] BinaryData = (byte[])data;
                    NativeMethods.RegSetValueEx(pHandle, name, 0, REG_BINARY, BinaryData, BinaryData.Length);
                    break;
                default:
                    throw new Exception("Unknown type to set!");
            }
        }

		///	<summary>			
		///	Retrieves a	specified subkey.			
		///	</summary>			
		///	<param name="subkey">Name or path of subkey	to open.</param>			
		///	<param name="writeable">Set	to true	if you need	write access to	the	key.</param>			
		///	<returns>The subkey	requested, or null if the operation	failed.</returns>			
		public RegistryKey OpenSubKey(string subKey, bool writeable)
		{
			try
			{			
				// Open the key using the fully-qualified path
				SafeRegistryHandle Result = NativeMethods.RegOpenKeyEx(pRootKey, pName + @"\" + subKey, 0, 0);
				
				// Yes.  Return the new key
				if(Result == null)
					return null;
				else
					return new RegistryKey(pRootKey, Result, pName + @"\" + subKey);
			}
			catch
			{
				throw;
			}
		}

		///	<summary>			
		///	Retrieves a	subkey as read-only.			
		///	</summary>			
		///	<param name="subkey">Name or path of subkey	to open.</param>			
		///	<returns>The subkey	requested, or null if the operation	failed.</returns>			
		public RegistryKey OpenSubKey(string subkey)
		{
			// just	call the overloaded	function				
			return OpenSubKey(subkey, false);
		}

		///	<summary>			
		///	Retrieves the name of the key.			
		///	</summary>			
		public string Name
		{
			get
			{
				return pName;
			}
		}

		///	<summary>			
		///	Retrieves a	string representation of this key.				
		///	</summary>			
		///	<returns>A string representing the key.	If the specified key is	invalid	(cannot	be found) then a null value	is returned.</returns>			
		public override string ToString()
		{
			return pName;
		}
	}

	///	<summary>		
	///	This class holds root RegistryKeys for the system registry.	 Use these keys	to manipulate the registry.		
	///	</summary>		
	public sealed class Registry
	{
		public static readonly RegistryKey CurrentUser = new RegistryKey(RootKey.HKEY_CURRENT_USER);
		public static readonly RegistryKey Users = new RegistryKey(RootKey.HKEY_USERS);
		public static readonly RegistryKey LocalMachine = new RegistryKey(RootKey.HKEY_LOCAL_MACHINE);
		public static readonly RegistryKey ClassesRoot = new RegistryKey(RootKey.HKEY_CLASSES_ROOT);
	}

	namespace IO
	{
		public sealed class SafeRegistryHandle : SafeHandle
		{
			//public static readonly SafeRegistryHandle Invalid = new SafeRegistryHandle(); 

			private SafeRegistryHandle() 
				: base(IntPtr.Zero, true)
			{ 
			}        

			public SafeRegistryHandle(IntPtr handle, bool ownsHandle)
				: base(IntPtr.Zero, ownsHandle)
			{
				SetHandle(handle);
			}	
			
#if PocketPC
			[DllImport("coredll.dll", EntryPoint = "RegCloseKey")]
			private static extern int RegCloseKeyCompact(IntPtr handle);
#endif

			[DllImport("advapi32.dll", EntryPoint = "RegCloseKey")
#if !PocketPC && Framework20
         , SuppressUnmanagedCodeSecurity
         , ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)
#endif
			]
			private static extern int RegCloseKeyDesktop(IntPtr handle);

			public override bool IsInvalid
			{
				get 
				{
					int HandleValue = handle.ToInt32();
					return HandleValue == 0 || HandleValue == -1;
				}
			}

			protected override bool ReleaseHandle()
			{
#if PocketPC
				if(Environment.OSVersion.Platform == PlatformID.WinCE)
					return RegCloseKeyCompact(handle) == 0;
				else
					return RegCloseKeyDesktop(handle) == 0;
#else
            return RegCloseKeyDesktop(handle) == 0;
#endif
			}

			//			public static implicit operator SafeRegistryHandle(IntPtr value)
			//			{
			//				return new SafeRegistryHandle(value);
			//			}
			//		
			//			public static implicit operator IntPtr(SafeRegistryHandle value)
			//			{
			//				return value.DangerousGetHandle();
			//			}

			public override string ToString()
			{
				return handle.ToString() + " ("
					+ (IsInvalid ? "Invalid" : "Valid")
					+ " and "
					+ (IsClosed ? "Closed" : "Open")
					+ ")";
			}
		}
	}
}
#endif